﻿using GN.Pay.Utils;
using GN.Pay.Utils.Reflection;
using GN.Pay.Utils.Reflection.Meta;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GN.Pay.TradeChannel
{
    /// <summary>
    /// 服务抽象
    /// </summary>
    [Serializable]
    public abstract class AService : IValidatableObject
    {
        /// <summary>
        /// 验证
        /// </summary>
        /// <param name="channel"></param>
        /// 
        public void Validate(IChannel channel)
        {
            var metaPropertys = MetaPropertys();
            foreach (MetaProperty p in metaPropertys)
            {
                object value = p.GetMemberValue(this);
                ValidationContext context = new ValidationContext(this)
                {
                    DisplayName = p.DisplayAttribute.Name,
                    MemberName = p.MemberInfo.Name
                };
                foreach (ValidationAttribute va in p.ValidationAttributes)
                {
                    try
                    {
                        va.Validate(value, context);
                    }
                    catch (Exception err)
                    {
                        throw channel.ThrowChannelValidationException(err.Message);
                    }
                }
            }
            IValidatableObject validatable = this;
            var items = validatable.Validate(new ValidationContext(this));
            if (items != null)
            {
                var validationResult = items.FirstOrDefault();
                if (validationResult != null)
                {
                    throw channel.ThrowChannelValidationException(validationResult.ErrorMessage);
                }
            }            
        }

        /// <summary>
        /// 获取成员元集合
        /// </summary>
        /// <returns></returns>
        public ISet<MetaProperty> MetaPropertys()
        {
            return ReflectionUtils.GetCacheMetaPropertys(this.GetType());
        }

        /// <summary>
        /// 获取固定长度字符容
        /// </summary>
        /// <param name="value">值</param>
        /// <param name="length">长度</param>
        /// <returns></returns>
        public static string FixedLengthString(string value, int length)
        {
            if (value == null)
            {
                return "";
            }
            value = value.Trim();
            if (value.Length > length)
            {
                return value.Substring(0, length);
            }
            return value;
        }

        /// <summary>
        /// 获取签名字典
        /// </summary>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public SignDictionary GetSignDictionary(IComparer<string> comparer)
        {
            SignDictionary dic = new SignDictionary(comparer);
            var items = this.MetaPropertys();
            foreach (var item in items)
            {
                object value = item.GetMemberValue(this);
                if (value != null)
                {
                    dic[item.PropertyInfo.Name] = value.ToString();
                }
            }
            return dic;
        }

        /// <summary>
        /// 获取整数金额(分)
        /// </summary>
        /// <param name="value">值</param>
        /// <returns></returns>
        public static long GetMoneyForCent(decimal value)
        {
            return (long)(Math.Round(value, 2) * 100m);
        }

        /// <summary>
        /// 加载属性
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="instance">实例</param>
        /// <param name="propertyValues">属性值集合</param>
        protected void LoadProperty<T>(object instance, IDictionary<string, T> propertyValues)
        {
            var ps = MetaPropertys();
            foreach (var p in ps)
            {
                T value;
                if (p.PropertyInfo.CanRead
                    && propertyValues.TryGetValue(p.PropertyInfo.Name, out value) && value != null)
                {
                    p.SetMemberValue(instance, DataConvertUtils.TargetValueConvert(p.PropertyInfo.PropertyType, value));
                }
            }
        }

        /// <summary>
        /// 加载属性
        /// </summary>
        /// <param name="propertyValues"></param>
        protected void LoadProperty<T>(IDictionary<string, T> propertyValues)
        {
            LoadProperty(this, propertyValues);
        }

        /// <summary>
        /// 获取普通金额
        /// </summary>
        /// <param name="value">值</param>
        /// <returns></returns>
        protected static decimal GetMoneyForGeneral(long value)
        {
            return ((decimal)value) / 100m;
        }

        /// <summary>
        /// 服务验证
        /// </summary>
        /// <param name="validationContext">验证上下文</param>
        /// <returns></returns>
        protected virtual IEnumerable<ValidationResult> ServiceValidate(ValidationContext validationContext)
        {
            return null;
        }

        IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
        {
            return ServiceValidate(validationContext);
        }

        /// <summary>
        /// 生成 Http 请求
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="inputDictionary"></param>
        /// <param name="url"></param>
        /// <param name="encoding"></param>
        /// <param name="method">Post 或 Get</param>
        /// <returns></returns>
        protected string BuildHttpRequest<TValue>(IDictionary<string, TValue> inputDictionary,
            string url, string encoding, string method)
        {           
            StringBuilder sbHtml = new StringBuilder();
            sbHtml.AppendFormat("<form id='paySubmit' name='paySubmit' action='{0}?_input_charset={1}' method='{2}'>", url, encoding, method);
            foreach (var item in inputDictionary)
            {
                sbHtml.AppendFormat("<input type='hidden' name='{0}' value='{1}'/>", item.Key, item.Value);
            }
            sbHtml.Append("</form>");
            sbHtml.Append("<script>document.forms['paySubmit'].submit();</script>");
            return sbHtml.ToString();
        }
    }
}
